#include "decoderCommunicator.hh"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> //client (gethostbyname())


#include <stdlib.h> //atoi atol ...etc
#include <string.h> //for string tokenizer
#include <sstream> // to convert integer to string to send
#include <iostream>




using namespace std;

decoderCommunicator::decoderCommunicator(int decoder_port, char* dataProxy_addr, int dataProxy_port){

    decoderPort = decoder_port;
    dataProxyPort = dataProxy_port;
  
    strcpy(dataProxyAddr,  dataProxy_addr);
    
}

e_status decoderCommunicator::runDecoderServer(){

    return runServerAndConnect("frontend", decoderPort, &sock, &connected);
}

// reads from the open port, and put all the received commands in a vector of strings. then it pops one element from the command vector and store results in k, and num_errors.
//return 0 if it is successfull, 1 if terminated
e_status decoderCommunicator::readFromFrontend(size_t* k, size_t* num_errors){
    
    char *stringToken ;
    string command;
    int bytes_recieved;
    int counts = 0;

    if((int)commandQueue.size() < 1){
    
        bytes_recieved = recv_to(connected,recv_data,DECODER_BUFFER_SIZE,0,DECODER_FRONTEND_TIMEOUT);
        //cout << "bytes " <<  bytes_recieved << endl;
        if(bytes_recieved == 0){ //means socket is closed!
            cerr << "frontend is disconnected!! " << endl;
            terminate();
            return DISCONNECTED;
        }else if(bytes_recieved == -1){
            cerr << "cannot read from frontend!!" << endl;
            terminate();
            return ERROR;
        }else if(bytes_recieved == -2){
            cerr << "frontend command timeout!!" << endl;
            return TIMEOUT;
        }
        recv_data[bytes_recieved] ='\0';//define end of the string

        cout << "[decoder communicator] frontend sent: " << recv_data << endl;
        stringToken = strtok(recv_data , "|");
        while( stringToken != NULL){

            command.assign(stringToken);
            commandQueue.push(command);
            stringToken = strtok(NULL,"|");
        }
    }
    if(commandQueue.size() >= 1){ 
    
	    if(commandQueue.front()[0] == 'd'){
	    
	        //fitch the content
	        stringToken = strtok((char*) commandQueue.front().c_str(), ",");
	        while(stringToken != NULL){
	            if(counts == 1)
	                *k = atoi(stringToken);
	            else if( counts == 2){
	                *num_errors = atoi(stringToken);
                    result_counter++;
                }
	            stringToken = strtok(NULL,",");// move to next token
	
	            counts ++ ;
	        }
	        //cout << "count : " << counts << endl;
	        if(counts != 3){
	
	            cerr << "few command arguments" << endl;
                    commandQueue.pop();
                    return ERROR;
	        }


                commandQueue.pop();
    
                return SUCCESS;


	    }else if( commandQueue.front()[0] == '$' ){
                commandQueue.pop();
	        return TERMINATED; //terminated
	    }else //wrong command syntax
	    {
	        cerr << "wrong command syntax" << endl;
	    
                commandQueue.pop();
                return ERROR;
	    }
	
	    
    return ERROR;

    }
}

e_status decoderCommunicator::writeToFrontend(integer res,size_t  errors){

    stringstream send_data;
    send_data <<  "r,"  << res <<  ','  << errors <<  '|';
    
    cout << "[decoder communicator] sending: " << send_data.str()<< endl;
    if (send(connected, send_data.str().c_str(),send_data.str().length(), 0) == -1)
    {   perror("cannot send to frontend");
        return ERROR;
    }
    return SUCCESS;
}

e_status decoderCommunicator::writeEndOfResultToFrontend(){
    cout << "[decoder communicator] sending: $"<< endl;
    if (send(connected, "$",2, 0) == -1)
    {   perror("failed to send $ to frontend");
        return ERROR;
    }
    return SUCCESS;
}
e_status decoderCommunicator::terminateDataProxy(){

    if (send(dataProxySock, "$",2, 0) == -1)
    {   perror("failed to send terminate command to dataProxy");
        return ERROR;
    }
    return SUCCESS;
}

e_status decoderCommunicator::terminate(){

    e_status res = SUCCESS;
    if (terminateDataProxy() == ERROR)
        res = ERROR;
    if (terminateSocket(dataProxySock)== ERROR)
        res = ERROR;
    if (terminateSocket(connected) == ERROR)
        res = ERROR;
    if (terminateSocket(sock)== ERROR)
        res = ERROR;
    return res;
}

e_status decoderCommunicator::connectToDataProxy(){

    return connectToClient("data proxy", dataProxyAddr, dataProxyPort, &dataProxySock);

}
e_status decoderCommunicator::readFromDataProxy(fixedBitSizeInteger* residue, fixedBitSizeInteger* prime){
            
    char *stringToken ;
    string command;
    int bytes_recieved;
    int counts = 0;

    if((int)resultQueue.size() < 1){
    
        bytes_recieved = recv_to(dataProxySock,recv_data,DECODER_BUFFER_SIZE,0, DECODER_DATAPROXY_TIMEOUT);
        if(bytes_recieved == 0){ //means socket is closed!
            cerr << "dataproxy  connection is lost!! " << endl;
            terminate();
            return DISCONNECTED;
        }else if(bytes_recieved == -1){
            cerr << "cannot read from data proxy !!" << endl;
            terminate();
            return DISCONNECTED;
        }else if(bytes_recieved == -2){
            cerr << "data proxy data timeout" << endl;
            return TIMEOUT;
        }
        
        recv_data[bytes_recieved] ='\0';//define end of the string

        stringToken = strtok(recv_data , "|");
        while( stringToken != NULL){

            command.assign(stringToken);
            resultQueue.push(command);
            stringToken = strtok(NULL,"|");
        }
    }
    if(resultQueue.size() >= 1){ 
    
	    if(resultQueue.front()[0] == 'r'){
	    
	        //fitch the content
	        stringToken = strtok((char*) resultQueue.front().c_str(), ",");
	        while(stringToken != NULL){
	            if(counts == 1)// the first should be r
                        *residue = atol(stringToken);
	            else if( counts == 2)
	                *prime = atol(stringToken);
	            
	            stringToken = strtok(NULL,",");//move to next token
	
	            counts ++ ;
	        }
	        if(counts != 3){
	
	            cerr << "few command arguments" << endl;
                    *residue = 0;
                    *prime = 0;
                    resultQueue.pop();
                    return ERROR;
	        }
                cout << "[decoder communicator] receiving[" << ++result_counter << "] : res "  << *residue << " prime " << *prime << endl; 


                resultQueue.pop();
    
                return SUCCESS;


	    }else //wrong command syntax
	    {
	        cerr << "wrong result syntax" << endl;
	    
                resultQueue.pop();
                return ERROR;
	    }
	
    }
        
	    
    return ERROR;
}

